﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ChainAnalises.Classes.AuxiliaryClasses.DataManipulators.Iterators;
using ChainAnalises.Classes.DivizionToAccords;
using ChainAnalises.Classes.DivizionToAccords.AuxiliaryClasses;
using ChainAnalises.Classes.DivizionToAccords.Criteria;
using ChainAnalises.Classes.DivizionToAccords.ProbabilityCountStrateges;
using ChainAnalises.Classes.IntervalAnalysis;
using Segmentator.Dividers;

namespace Segmentator.Dividers
{
   
    /// <summary>
    /// Класс расчитывает расхождение std(Wj) фактической и расчетной характеристик вхождений испытуемого псевдослова
    /// в составе текста
    /// </summary>
    class StdCalculator
    {
        /// <summary>
        /// Метод подсчёта частоты вхождения слова
        /// </summary>
        private Method _calcFrecuncyMethod;
        /// <summary>
        /// Привязка к месту, откуда будет смещаться окно(начало,  середина, конец)
        /// </summary>
        private LinkUp _intervalLink;

        private double _balance_factor = 0;
        /// <summary>
        /// Все возможные вхождения слова длиной n и их позиции в цепи
        /// </summary>
        private IDataCollector fullEntry;

        private IDataCollector less_one;

        private IDataCollector less_two;

        public static EntryCollector _collector;
        public StdCalculator(Method method, LinkUp linkUp, double balance_factor)
        {
            _calcFrecuncyMethod = method;
            _intervalLink = linkUp;
            _balance_factor = balance_factor;
        }

        public SortedDictionary<double, DictionaryEntry> Alphabet = new SortedDictionary<double, DictionaryEntry>();
        /// <summary>
        /// Возвращает найденое слово, которое прошло по заданной характеристике
        /// </summary>
        /// <param name="convolutedChain">Ранее свёрнутая цепочка или исходная</param>
        /// <param name="currentWindowLen">Текущая длина окна сканирования</param>
        /// <param name="alphabet">Текущий словарь</param>
        /// <param name="level">Некоторое значение порога, по которому псевдослово определяется как слово</param>
        /// <returns>Слово - число вхождений</returns>
        public DictionaryEntry? CalcStds(Chain convolutedChain, int currentWindowLen, AlphabetChain alphabet, float level)
        {
            fullEntry = DataCollectorFactory.Create(currentWindowLen);
            less_one = DataCollectorFactory.Create(currentWindowLen - 1); // вхождение подслов с -1 символом справа или слева
            less_two = DataCollectorFactory.Create(currentWindowLen - 2); // вхождение подслов с -1 символом и справа и слева

            IteratorStart<Chain, Chain> inspector = new IteratorStart<Chain, Chain>(convolutedChain, currentWindowLen, 1);
            bool flag = false;
            while (inspector.Next())
            {
                for (int i = 0; i < inspector.Current().Length; i++)
                {
                    if ((inspector.Current()[i] is Chain))
                    {
                        if (((Chain)inspector.Current()[i]).Length > 1)
                        flag = true;
                    }
                }

                if (flag)
                {
                    flag = false;
                    continue;
                }
                fullEntry.Add(inspector, 0);

                Strategy Method = GetStrategyForLessOne.Get(inspector, currentWindowLen);  // выбор сдвига итератора
                Method.Calculate(less_one, inspector.Current(), inspector.ActualPosition());
                Method = GetStrategyForLessTwo.Get(inspector, currentWindowLen); // выбор сдвига итератора
                Method.Calculate(less_two, inspector.Current(), inspector.ActualPosition());
            }


            CriteiaMethod CriteriaCalculator = CriteriaStrategy.Get(_calcFrecuncyMethod); // очень серьезный калькулятор
            Cross cross = new Cross();
            Filter Filter = new SimpleFilter(); // вот хз
            foreach (DictionaryEntry accord in fullEntry) // смотрим на собранные данные в виде ключ - значение
            {
                //DictionaryEntry accord = new DictionaryEntry(accord.Key, accord.Value);

                if ((IDataForStd)accord.Value == null && less_one.Size() == 0 && less_two.Size() ==0) break;
                               
                Filter.MakeFilteration((IDataForStd)accord.Value, convolutedChain.Length, currentWindowLen); // значение, длина цепи, длина окна. не понятно что фильтрует
           //    cross.MakeCross((IDataForStd)accord.Value, currentWindowLen);
                double FrequenceCriteria = CriteriaCalculator.Frequncy((IDataForStd)accord.Value, convolutedChain.Length, currentWindowLen); // посчитали частоту

                double DezignExpected =
                    CriteriaCalculator.DesignExpected((Chain)accord.Key, (IDataForStd)accord.Value, convolutedChain.Length,
                                                      currentWindowLen, less_one, less_two);
                double IntervalEstimate =
                    CriteriaCalculator.IntervalEstimate((Chain)accord.Key, (IDataForStd)accord.Value, convolutedChain.Length,
                                                        currentWindowLen, _intervalLink); // посчитали интервальную частоту
                double std =
                    (Math.Abs(_balance_factor * IntervalEstimate + (1 - _balance_factor) * FrequenceCriteria - DezignExpected)) /
                    Math.Sqrt(DezignExpected); // используем баланс фактор для оценки учитывания в большую или меньшую сторону обычной частоты или интервальной

                if (!Alphabet.ContainsKey(std)) Alphabet.Add(std, accord);
            }

            ArrayList tmp = new ArrayList(Alphabet.Keys);
            ArrayList tmp2 = new ArrayList(Alphabet.Values);
            double best_std = -1;
            bool iscompound = false; // является ли принятое слово составным(из нескольких цепей)
            for (int i = tmp2.Count - 1; i >= 0; i--)
            {
                if (!one_item_is_in_alphabet(((DictionaryEntry)tmp2[i]).Key as Chain, alphabet)) // если в алфавите нет вхождений данной цепи
                {
                    best_std = (double)tmp[i];
                    if (best_std > level) // если новое значение превышает порог, то полученный алфавит клевый
                    {
                       /* for (int j = 0; j < (((DictionaryEntry)tmp2[i]).Key as Chain).Length; j++)
                        {
                            try
                            {
                                if (((Chain) ((((DictionaryEntry) tmp2[i]).Key as Chain)[j])).Length > 1)
                                {
                                    iscompound = true;
                                }
                              

                            }
                            catch (Exception e)
                            {

                                
                            }
                        }
                          if (iscompound)
                          {
                              iscompound = false;
                              continue;
                          }*/
                        return Alphabet[best_std];
                    }
                    else
                    {
                        return null; // иначе херня
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Проверяет содержится ли в алфавите одна из последовательностей цепи
        /// </summary>
        /// <param name="chain"></param>
        /// <param name="previos_alph"></param>
        /// <returns></returns>
        private bool one_item_is_in_alphabet(Chain chain, AlphabetChain previos_alph)
        {
            for (int i = 0; i < chain.Length; i++)
            {
                if (previos_alph.Contains(chain[i])) return true;
            }
            return false;
        }


    }
}
